using System;
using System.Collections.Generic;

namespace Developex.DatabaseWrapper.CSVImportExport
{
	///<summary>
	///</summary>
	///<typeparam name="T"></typeparam>
	public class EIConnectedField<T> : EIField where T : Row, new()
	{
		/// <summary>
		/// 
		/// </summary>
		public List<EIField> OtherFields = new List<EIField>();

		///<summary>
		///</summary>
		///<param name="name"></param>
		///<param name="displayName"></param>
		///<param name="connectedField"></param>
		public EIConnectedField(string name, string displayName, string connectedField)
			: this(name, displayName, connectedField, false)
		{
		}

		///<summary>
		///</summary>
		///<param name="name"></param>
		///<param name="displayName"></param>
		///<param name="connectedField"></param>
		///<param name="createIfNotExist"></param>
		public EIConnectedField(string name, string displayName, string connectedField, bool createIfNotExist)
			: base(name, null, displayName)
		{
			this.connectedObjectField = connectedField;
			this.createIfNotExist = createIfNotExist;
		}

		/// <summary>
		/// 
		/// </summary>
		protected string connectedObjectField;
		/// <summary>
		/// 
		/// </summary>
		protected bool createIfNotExist;

		///<summary>
		///</summary>
		public string ConnectedObjectField
		{
			get { return connectedObjectField; }
			set { connectedObjectField = value; }
		}

		///<summary>
		///</summary>
		public override object Value
		{
			get
			{
				GetConnectionEventArgs e = OnGetConnection();
				if (e != null)
				{
					if (e.CloseConnection)
					{
						using (e.Conn)
						{
							return GetPrimaryKey(e.Conn);
						}
					}

					return GetPrimaryKey(e.Conn);
				}
				return null;
			}
			set
			{
				base.Value = value;
			}
		}

		#region GetConnection routine

		///<summary>
		/// Function wich Returns Connection 
		///</summary>
		public delegate void GetConnectionDelegate(object sender, GetConnectionEventArgs e);

		/// <summary>
		/// Event which fired when object needs to get open Connection
		/// If this event not implemented, than object don't work properly
		/// </summary>
		public event GetConnectionDelegate GetConnection;

		/// <summary>
		/// 
		/// </summary>
		public class GetConnectionEventArgs : EventArgs
		{
			private Connection conn;
			private bool closeConnection;

			/// <summary>
			/// Get/Set connection
			/// </summary>
			public Connection Conn
			{
				get { return conn; }
				set { conn = value; }
			}

			/// <summary>
			/// Get/Set Close Connection property
			/// </summary>
			public bool CloseConnection
			{
				get { return closeConnection; }
				set { closeConnection = value; }
			}

			/// <summary>
			/// Default Contructor
			/// </summary>
			/// <param name="conn"></param>
			/// <param name="closeConnection"></param>
			public GetConnectionEventArgs(Connection conn, bool closeConnection)
			{
				this.conn = conn;
				this.closeConnection = closeConnection;
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		protected GetConnectionEventArgs OnGetConnection()
		{
			if (GetConnection != null)
			{
				GetConnectionEventArgs e = new GetConnectionEventArgs(null, true);
				GetConnection.Invoke(this, e);
				if (e.Conn != null)
					return e;
			}

			return null;
		}

		#endregion

		///<summary>
		///</summary>
		///<param name="conn"></param>
		///<returns></returns>
		///<exception cref="EIException"></exception>
		public int? GetPrimaryKey(Connection conn)
		{
			T connectedObject = new T();
			if (connectedObject.Fields.ContainsKey(connectedObjectField))
			{
				object baseValue = value;
				if (ImportHeadersByName != null && ImportValuesString != null)
					baseValue = ImportValuesString[ImportHeadersByName[DisplayName]];

				if (baseValue == null || baseValue.ToString() == "")
					return null;

				connectedObject.Fields[connectedObjectField].SetValue(EIUtils.GetConvertedValue(connectedObject.Fields[connectedObjectField], baseValue.ToString()));

				if (OtherFields.Count > 0)
				{
					foreach (EIField field in OtherFields)
					{
						field.ImportHeadersByName = ImportHeadersByName;
						field.ImportValuesString = ImportValuesString;
						connectedObject.Fields[field.Name].SetValue(field.Value);
					}
				}

				if (Load != null)
				{
					LoadEventArgs args = new LoadEventArgs(conn, connectedObject);
					Load(this, args);
					if (args.Successed)
						return args.LoadedId;
				}
				else if (connectedObject.LoadByAssignedFields(conn) == OperationResult.Success)
				{
					return (int?)connectedObject.PrimaryKeyField.GetValue();
				}

				if (createIfNotExist)
				{
					if (connectedObject.Insert(conn) == OperationResult.Success)
						return (int?)connectedObject.PrimaryKeyField.GetValue();
					throw new EIException("Unable to create connected object");
				}
				throw new EIException("Unable to load connected object");
			}
			throw new EIException("Unable to load connected object");
		}

		/// <summary>
		/// 
		/// </summary>
		public event LoadDelegate Load = null;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		public delegate void LoadDelegate(object sender, LoadEventArgs args);

		/// <summary>
		/// 
		/// </summary>
		public class LoadEventArgs : EventArgs
		{
			/// <summary>
			/// [OUT] is successed
			/// </summary>
			public bool Successed = true;
			/// <summary>
			/// [OUT] loaded id
			/// </summary>
			public int? LoadedId = null;
			/// <summary>
			/// open connection
			/// </summary>
			public Connection Connection;
			/// <summary>
			/// connection object (initialized but not loaded)
			/// </summary>
			public T ConnectedObject;
			/// <summary>
			/// Constructor
			/// </summary>
			/// <param name="conn"></param>
			/// <param name="connectedObject"></param>
			public LoadEventArgs(Connection conn, T connectedObject)
			{
				this.Connection = conn;
				ConnectedObject = connectedObject;
			}
		}

	}
}